home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 451-475 / disk_463 / ilbm / ilbmlib.doc < prev    next >
Text File  |  1992-05-06  |  68KB  |  1,352 lines

  1. Set your editor's TAB width to 3
  2.  
  3.                              THE IFF ILBM LIBRARY MANUAL
  4.                             ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
  5.                                      by Jeff Glatt
  6.                                  6 Sycamore Drive East
  7.                                  New Hartford, NY 13413
  8.  
  9. *****************************************************************************
  10.  
  11.     1).  Introduction
  12.     2).  Overview
  13.     3).  High Level Routines
  14.     4).  Middle Level
  15.     5).  Handling PROPS
  16.     6).  Custom FORM Handler
  17.     7).  Custom (ILBM) Chunk Handler
  18.     8).  Low Level
  19.     9).  Adapting old applications
  20.     10). ANIM Support
  21.     11). Image Size/Scaling
  22.     12). ILBMLib Error Msgs
  23.     13). Misc Routines
  24.     14). Additional Comments
  25.     15). Acknowledgements
  26.     16). Scary Legal Stuff
  27.  
  28.  
  29. *****************************************************************************
  30. 1). Introduction
  31.  
  32.     The primary purpose of the ilbm.library is to provide functions to read and
  33. write any kind of IFF file.  In particular, there is extra, high level support
  34. for ILBM picture files which simplifies the viewing or saving of a picture.
  35. Furthermore, there are routines useful for creating an ANIM reader/writer/
  36. player.
  37.     The ilbm.library is a disk-based runtime library.  This means that any
  38. application, upon being run, can open and call routines in this library just
  39. like a program might utilize routines in the Intuition or Graphics libraries. 
  40. The ilbm library is easily accessed from C, assembly, and BASIC programs, and
  41. there are examples available in each language.
  42.     The library is based upon the original Electronic Arts IFF code (i.e. Open-
  43. RIFF, GetBODY, etc.) as well as a few Commodore enhancements (i.e. Scheppner's
  44. getBitMap, handleCAMG, etc.).  There have been a few changes to the internal
  45. logic of some routines, but generally, they perform the same functions as the
  46. original code.  A few more significant changes were made to certain routines in
  47. order to accomodate ANIM and non-ILBM files, and also to streamline the parsing
  48. of LISTs, CATs, and PROPs.
  49.     The biggest change between the original code and this library is that the
  50. code has been rewritten in the tightest possible 68000 assembly.  The size of
  51. the library is < 7000 bytes.  A program that uses this library can be smaller
  52. and faster than if the program used the original EA code.¹  Because the library
  53. uses no global data and is therefore re-entrant, many applications can use the
  54. library simultaneously.
  55.     Finally, access to the low level IFF functions is provided.  The routines,
  56. OpenRIFF, GetChunkHdr, IFFReadBytes, IFFWriteBytes, etc. are all included.
  57. These routines are equivilent to the original EA code except that the lib code
  58. is smaller and faster.  Because of this, it is possible to use this library for
  59. any kind of IFF reader and writer with a resulting improvement in the applica-
  60. tion's size and speed.
  61.     The current version is 0.5.
  62.  
  63.  
  64. *****************************************************************************
  65. 2)  Overview
  66.  
  67.     The library has functions that fall into three levels: high, middle, and
  68. low.  The high level is designed to read or write ILBM files with tremendous-
  69. ly little effort on the part of the application.  The middle level requires
  70. more setup, but allows you to construct a reader/writer for any kind of IFF
  71. file.  This level handles extremely ugly details like LISTs, CATs, error
  72. checking for IFF "weirdness", and DOS I/O while allowing you control over what
  73. is to be done with individual chunks.  The low level maintains DOS I/O and
  74. error checking, but leaves you to deal with LISTs and CATs.  Anything that you
  75. used to do with the original EA code, you should be able to do in an almost
  76. identical manner, and adapting your present IFF code to use the library is
  77. quite easy.
  78.     Most library routines return an IFFP code.  This is a number from 0 to -11,
  79. or in some cases, a 4 byte IFF ID.  0 (IFF_OKAY) usually means success.  The
  80. negative numbers mean errors.  See the INCLUDE files for all possible numbers. 
  81.  
  82.  
  83. *****************************************************************************
  84. 3). High Level Routines
  85.  
  86.     At the highest levels, the type of IFF file that this library is designed
  87. to read and write is an ILBM.
  88.     There are 2 high level functions, LoadIFFToWindow() and SaveWindowToIFF().
  89. The former loads an ILBM into a window, the latter saves a window as an ILBM.
  90. These functions are so comprehensive that they can even be used with inter-
  91. preted languages like BASIC or Rexx.
  92.     For saving a file, SaveWindowToIFF() is passed the filename that you
  93. wish to create, and the pointer to the window (whose screen's bitplanes are)
  94. to be saved in IFF ILBM form.  The library writes out a complete ILBM with
  95. the proper BMHD, CMAP, CAMG, and BODY chunks.  All error checking is done by
  96. the lib.  SaveWindowToIFF() returns an IFFP number.  0 means that the save
  97. was successful.  Any other number is an error.  In the case of error, any
  98. partial file is deleted.
  99.     When reading a file, LoadIFFToWindow() takes the filename that you wish
  100. to load, and a special ILBMFrame structure. See the INCLUDE files for a
  101. description of this structure. The C include file is "ILBM_lib.h". The assembly
  102. include is "IFF.i". Basic users should read "BasicUsers".
  103.     You MUST allocate this structure and initialize certain fields before
  104. using LoadIFFToWindow().
  105.     LoadIFFToWindow() looks for ILBMs inside of CATs and LISTs, sidestepping all
  106. of the other non-ILBM FORMs.  It rummages around inside of such a file looking
  107. for the first ILBM that it can find.  If it finds an ILBM, it does one of two
  108. things:
  109.  
  110.     1). Loads the imagery into a window that you opened, scaling the picture
  111.          to fit the dimensions of the window if that is necessary.  What this
  112.          means is that you can load a HIRES image into an opened LORES window,
  113.          and vice versa, etc.
  114.  
  115.     2). Opens a screen and borderless backdrop window that best "fits" the
  116.          picture (e.g. the proper width, height, depth, and viewmodes.
  117.  
  118.     You determine which method is imployed by how you set up the ILBMFrame
  119. before calling LoadIFFToWindow().  The ILBMFrame has iWindow and iScreen
  120. fields.  If you zero both these fields, then the library will open a screen
  121. and window into which it loads the picture.  The pointers for this new screen
  122. and window will be stored in their respective ILBMFrame fields.  The window
  123. will have a default IDCMP of MOUSEBUTTONS.  If instead, you open your own
  124. window and screen, you should place these addresses into the ILBMFrame fields
  125. before calling LoadIFFToWindow().  The lib will then load the image into that
  126. window.
  127.     Furthermore, you should set up the iUserFlags field.  When certain bits of
  128. this field are set, the library will do such things as make the mouse pointer
  129. invisible, or not change your window's present colormap, etc.  See the INCLUDE
  130. files for details.  Usually, you'll just zero iUserFlags.
  131.     You do not have to initialize any of the other ILBMFrame fields.
  132.     LoadIFFToWindow() returns an IFFP number.  0 means that the load was
  133. successful.  Any other number is an error.
  134.     Note that if the library opens the window/screen for you, the pointers are
  135. returned in the ILBMFrame.  From this point on, these are your responsibility.
  136. You may ModifyIDCMP() the window, or anything else you desire.  You must close
  137. the window and screen when you are finished with them.  Even if LoadIFFToWin-
  138. dow() returns an error, you must still get the iWindow and iScreen fields, and
  139. close any non-zero pointer.  See the example programs for details.
  140.     If neither high level routine serves your purpose, then you will need to
  141. use some mid or low level routines instead.
  142.  
  143. ============================ SaveWindowToIFF ===========================
  144.  IFFP = SaveWindowToIFF(fileName, window)
  145.   d0                       d1       a0
  146.  
  147.  Saves a window's image as an IFF ILBM file. Returns an IFFP code (0 if OK,
  148.  a negative, non-zero number for an error. Z-flag set accordingly.)
  149.  This procedure calls SaveILBM(), passing in an <x, y> location of <0, 0>,
  150.  and a NULL mask. It also assumes you want to write out all the bitplanes
  151.  in the BitMap. Also, it applies byte run compression to the BODY.
  152.  If an error in saving, it deletes the partial file. The fileName should be
  153.  a complete path as might be typed at the CLI (i.e. df0:extras/myName). This
  154.  is an ascii, NULL-terminated string. window is the address of the opened
  155.  window (as returned from Intuition's OpenWindow).
  156.  
  157. ============================= LoadIFFToWindow ===========================
  158. IFFP = LoadIFFToWindow( fileName, ILBMFrame )
  159.  d0                        d1       a1
  160.  
  161.  Loads an ILBM file into the ILBMFrame's iWindow. If iWindow is NULL, opens
  162.  a screen/backdrop window for the image. This routine calls LoadILBM().
  163.  
  164.  
  165. ******************************************************************************
  166. 4). Middle Level
  167.  
  168.     The middle level routines isolate you from dealing with low level struc-
  169. tures, LISTs, and CATs, but give you the option of installing custom routines
  170. to handle FORMs, PROPs, or chunks inside of ILBM FORMs.
  171.     There are two mid level routines for reading/writing ILBMs, LoadILBM()
  172. and SaveILBM().  A third routine, LoadIFF(), is used for non-ILBM forms
  173. such as SMUS.  There is a routine that makes saving ANIMs easy, SaveANIM().
  174. Furthermore, LoadILBM() can easily be setup to parse an ANIM file.
  175.     We will discuss LoadILBM() and SaveILBM() first.
  176.  
  177.     ========================== SaveILBM() ==============================
  178. IFFP=SaveILBM(ViewModes,Compress,fileHandle,Mask,colors,BitMap,xyPoint,handler)
  179.  d0                    d0            d1            d2            d3        a0         a1        a2         a3
  180.  
  181.     ViewModes    - The viewmodes of the screen as an ULONG
  182.     Compress        - Compression type. 0=None 1=cmpByteRun1
  183.     fileHandle    - DOS handle of the opened file
  184.     Mask            - pointer to a mask plane, or NULL if none
  185.     colors        - pointer to the colorTable to save as a CMAP chunk
  186.     BitMap        - pointer to the Bitmap structure whose planes are to be saved
  187.                     as the ILBM BODY
  188.     xyPoint        - pointer to a structure holding two UWORD values that describe
  189.                     the xy position to be saved in the BMHD chunk
  190.     handler        - pointer to a routine to be called before the BODY is written
  191.                     out, or NULL if none
  192.  
  193.     SaveILBM() writes an entire BitMap as a FORM ILBM in an IFF file. Unlike
  194. SaveWindowToIFF, this routine allows you to save an uncompressed image, or with
  195. a different xy position than 0,0 (for saving a portion of the whole image),
  196. or saving additional chunks in addition to BMHD, CAMG, CMAP, and BODY.  It
  197. works for any display mode.
  198.     You must open the IFF file to be written before calling SaveILBM(), and
  199. close the file upon return.
  200.     SaveILBM() writes out a BMHD whose values are determined by the passed
  201. BitMap's size/depth and the xyPoint.  A CAMG chunk is written for the passed
  202. ViewModes.  A CMAP chunk is written for the passed colTable.
  203.     Then, if you have supplied a passed handler, it is called.  This allows an
  204. application to save additional chunks such as CRNG, etc.  The lib passes your
  205. handler a GroupContext structure.  To write a chunk, you must open the Context
  206. (via OpenRGroup()) and use PutCk(), PutCkKnown(), or calls to IFFWriteBytes
  207. (after PutCkHdr, and ending with PutCkEnd).  Your handler should return
  208. IFF_OKAY after it has successfully written out the desired chunks.  Any other
  209. IFFP code will terminate SaveILBM() with that error.
  210.     Finally, SaveILBM() writes the BODY based on your passed compress mode
  211. and your mask plane.
  212.     SaveILBM returns IFF_OKAY if successful. If an error, you must delete the
  213. partial file yourself.
  214.     The utility program IFFCheck would print the following outline of the
  215. resulting file:
  216.  
  217.     FORM ILBM
  218.         BMHD
  219.         CAMG
  220.         CMAP
  221.         ...other chunks that your handler saves
  222.         BODY        (compressed) or (uncompressed)
  223.  
  224.  
  225.     =========================== LoadILBM() ===========================
  226.     IFFP = LoadILBM(fileHandle, vectors, iframe)
  227.     d0                            d1            a0            a1
  228.  
  229.     fileHandle    - DOS handle of an opened file
  230.     vectors        - a pointer to a Vectors structure
  231.     iframe        - a pointer to the master, initialized ILBMFrame structure
  232.  
  233.     Can read an ILBM file. Unlike LoadIFFToWindow, you can arrange for your own
  234.  custom routines to handle FORMs, PROPs, and certain ILBM chunks.  LoadILBM()
  235.  will return IFF_DONE if an image has been successfully loaded into a window by
  236.  the lib's default 'FORM' handler.  All other IFFP codes indicate that an image
  237.  wasn't loaded.
  238.     In the case of custom handlers, normal return is IFF_OKAY when the whole
  239.  file has been scanned.  On the other hand, your custom handler can cause
  240.  LoadILBM() to terminate by returning any IFFP error (i.e. IFF_DONE, END_MARK,
  241.  IFF_BAD, etc).  In this case, LoadILBM() returns that IFFP error.
  242.     ILBMFrame's iWindow, iScreen, and iUserFlags must be initialized.
  243.  
  244.     EXTREMELY modified version of ReadPict.c
  245.     by Jerry Morrison, Steve Shaw, and Steve Hayes, Electronic Arts.
  246.     Modified by C. Scheppner and Jeff Glatt
  247.  
  248.  
  249.     The library has default routines that it calls whenever it encounters a
  250. LIST, CAT, PROP, FORM, or a chunk inside of a PROP or FORM.  LISTs, CATs, and
  251. ILBM PROPs are always handled by the library.  On the other hand, you can
  252. replace the default handlers for non-ILBM PROPs, all FORMs, or all ILBM chunks
  253. with your own custom routines.  For example, the library can call your FORM
  254. handler each time that it encounters a FORM inside of an IFF file.
  255.     Let me explain some details about the default routines.  First, the default
  256. FORM handler looks for ILBMs.  It can be setup to parse ANIMs and other non-
  257. ILBM forms, but it always looks for ILBMs.  For this reason, you should always
  258. supply an ILBMFrame when using the default FORM handler. The handler can find
  259. ILBMs inside of CATs and LISTs, sidestepping all of the other non-ILBM FORMs. 
  260. It rummages around inside of such a file looking for the first ILBM that it can
  261. find.     It handles the following chunks inside an ILBM:
  262.  
  263.     CMAP    BMHD    CRNG    CCRT    CAMG    BODY
  264.  
  265. The CMAP, BMHD, CAMG, CCRT, and CRNG chunks are loaded into the respective
  266. fields of the ILBMFrame (see INCLUDE file).  CCRT are converted to CRNG.  All
  267. other ILBM chunks are ignored.  When it finds the BODY of an ILBM, it does one
  268. of two things:
  269.  
  270.     1). Loads the imagery into a window that you opened, scaling the picture
  271.          to fit the dimensions of the window if that is necessary.
  272.  
  273.     2). Opens a screen and borderless backdrop window that best "fits" the
  274.          picture (e.g. the proper width, height, depth, and viewmodes).
  275.  
  276. This should sound familiar. It is exactly the same as LoadIFFToWindow.  Just
  277. like that high level function, you should set up the ILBMFrame according to
  278. what you want the lib to do.  After the image is loaded, the parsing is stopped
  279. and LoadIFF() returns IFF_DONE if successful.  All other error codes indicate
  280. failure.
  281.     The default PROP handler handles the following chunks inside an ILBM PROP:
  282.  
  283.     CMAP    BMHD    CRNG    CCRT    CAMG
  284.  
  285. These chunks are loaded into the respective fields of the ILBMFrame with CCRT
  286. converted to CRNG. All other ILBM chunks are ignored.  Also, any non-ILBM
  287. PROPs are ignored.
  288.     One of the parameters to LoadILBM() is a Vector structure. This is simply
  289. a structure that holds the pointers to whatever routines you would like the
  290. lib to execute while parsing an IFF file.  These routines replace the lib's
  291. default handlers.  If the Vector pointer is 0, then the default lib routine is
  292. used.  You must initialize this structure before calling LoadILBM()!
  293.     One field holds a pointer to a routine to handle non-ILBM 'PROP's or an
  294. ILBM PROP chunk that is "unknown" to the lib.  The lib doesn't know about the
  295. following ILBM chunks: ANHD, DEST, GRAB, SPRT, and DLTA.  It will skip these
  296. in an ILBM PROP if you don't install a PROPhandler. Also, it will skip nonILBM
  297. PROPs if you don't have a PROPhandler.  If you don't care, set this field to
  298. NULL.
  299.     Another field is for your FORMhandler routine address. If not NULL, this
  300. routine is called instead of the lib's default 'FORM' handler. You are expected
  301. to handle all parsing of FORMs as the library finds them.  This means that you
  302. also have to parse the chunks in the FORM.  If your application does not deal
  303. with ILBMs at all, you will have to install a custom FORM routine (and use
  304. LoadIFF).
  305.     Another field in the Vectors structure is for a CHUNKhandler. This is called
  306. by lib's default 'FORM' handler for all chunks inside an ILBM.  If NULL, the
  307. lib's CHUNKhandler will skip the following ILBM chunks:
  308.  
  309.     ANHD    DEST    GRAB    SPRT    DLTA
  310.  
  311. If you don't use the lib's default 'FORM' handler, then this field is ignored. 
  312. If you supply a CHUNKhandler, you are telling the library, "If you come across
  313. an ID that you don't understand, don't skip it. Instead, give it to me and I'll
  314. take care of that ILBM chunk".
  315.     The last field is for the NonILBMhandler.  This is called by the lib's
  316. default 'FORM' handler when it encounters a FORM other than an ILBM (i.e. 8SVX,
  317. etc).  If NULL, nonILBM FORMs are skipped over in search of ILBMs.  If you
  318. don't use the lib's default 'FORM' handler, then this field is ignored.  If you
  319. supply a NonILBMhandler, you are telling the library, "If you come across a
  320. FORM other than ILBM, don't skip it. Instead, give it to me and I'll take care
  321. of that FORM".  Note that the library still looks for an ILBM to load into a
  322. window when you use the default 'FORM' routine.
  323.     So, by installing routines via the Vectors structure, you can "take away"
  324. certain parsing duties from the lib, while it handles LISTS, CATS, and low
  325. level details.  For example, you could install just a CHUNKhandler and set
  326. the other fields to 0.  The lib would parse all LISTS, PROPS, CATS, and FORMS.
  327. It would only call your routine for each chunk in an ILBM.  See the examples,
  328. ANIMInfo.asm and IFFinfo.c, for details of installing custom routines, and
  329. using mid level routines.
  330.     If your application does not deal with ILBMs, you will need to replace the
  331. default FORMhandler and PROPhandler.  If you are dealing with ANIMs, you will
  332. only need a custom CHUNKhandler to handle DLTA and ANHD chunks, plus a custom
  333. PROPhandler for those chunks.  If you want to load other forms in addition to
  334. ILBMs, then you will need to add a NonILBMhandler.
  335.     Here are the parameters passed to your custom vectors. Return an IFFP code.
  336.  
  337.     IFFP = PROPhandler(chunkID,PropID,Context,Vectors,Frame,PROPList)
  338.     d0                            d0            d2        a0            a2        a3        a4
  339.  
  340.     IFFP = FORMhandler(chunkID,Context,Vectors,Frame,PROPList)
  341.     d0                            d0            a0         a2        a3        a4
  342.  
  343.     NonILBMhandler and CHUNKhandler same args as FORMhandler.
  344.  
  345.     LoadILBM() zeros out the ILBMFrame's iFlags (but not iUserFlags), iBMAP,
  346. iNumColors, and iCycleCnt fields.  You must initialize the iUserFlags field
  347. prior to calling this routine.  Various bits of this field affect certain
  348. options (see Include File for details).  Other bits are set by the lib to
  349. inform you of certain facts. (i.e. the ANIMB bit would be set by the default
  350. 'FORM' handler if an ANIM FORM was encountered in the file.)  You also initial-
  351. ize the iWindow and iScreen fields as per LoadIFFToWindow().
  352.  
  353.  
  354.  
  355.         ====================== LoadIFF() =====================
  356.  
  357.     IFFP = LoadIFF(file, vector, dataAddress)
  358.     d0                    d1            a0            a1
  359.  
  360.     This is just like LoadILBM() except that it is for reading/writing non-ILBM
  361. forms. If you're using this routine, you should definitely set up the Vector's
  362. FORMhandler to point to a custom routine.  Your custom routine should then
  363. parse FORMs using low level functions such as GetFChunkHdr, IFFReadBytes, etc.
  364. The dataAddress parameter is optional. It could be a pointer to a Frame of some
  365. sort (or it could be anything).  The lib will pass this to your FORMhandler
  366. as the Frame parameter. It's up to your FORMhandler to decide what it really is
  367. and what to do with it.
  368.     Normal return is IFF_OKAY if whole file scanned.  On the other hand, your
  369. FORMhandler can cause LoadIFF to terminate by returning any IFFP error (i.e.
  370. IFF_DONE, END_MARK, IFF_BAD, etc).  In this case, LoadIFF() returns that IFFP
  371. error.
  372.  
  373.         ====================== SaveANIM() ======================
  374. IFFP = SaveANIM(ViewModes,Compress,fileHandle,Mask,colors,BitMap,xyPoint,
  375.  d0                        d0            d1            d2            d3        a0        a1            a2
  376.  
  377.        FrameHandler,ANHDaddress)
  378.                 a3                a4
  379.  
  380.  Writes an ANIM file.  Writes the passed BitMap as the first frame (FORM
  381.  ILBM).  Assumes user wants to write out all planes of the BitMap.  Calls
  382.  application FrameHandler() for writing subsequent frames.  Normal return
  383.  result is IFF_OKAY.  Works for any display mode.
  384.  
  385.  The utility program IFFCheck would print the following outline of the
  386.  resulting file:
  387.  
  388.  LIST
  389.     PROP ILBM
  390.         BMHD
  391.         CAMG
  392.         CMAP
  393.         ANHD        ;if passed in
  394.     FORM ANIM
  395.         FORM ILBM
  396.             BODY    (compressed) or (uncompressed)
  397.         ....application routine's saved chunks (i.e. additional FORM ILBMs)
  398.  
  399.     This is similiar to SaveILBM except your FrameHandler is called repeatedly
  400.     (so you can write numerous frames) while return = IFF_OKAY.  Returning
  401.     a 1 stops the Handler loop successfully.  Returning an IFFP error aborts
  402.     the save.  Note that the file written is a LIST with a PROP ILBM.  This is
  403.     so that each frame need not contain identical data.  If the passed ANHD
  404.     address is not NULL, the ANHD chunk will be written in the PROP.  In this
  405.     way, a simple ANIM can be written where the first frame need only contain
  406.     a BODY chunk, and subsequent frames contain a DLTA.  If all successful,
  407.     final return is IFF_OKAY.  If an error, you must delete the partial file.
  408.     If no FrameHandler, pass a 0.
  409.  
  410.  
  411. ******************************************************************************
  412. 5). Handling PROPs
  413.  
  414.     LoadILBM() (or LoadIFF()) creates a special PROPList to take care of any
  415. PROPS in the file.  A PROPList is a linked list structure used to manage Frames
  416. allocated to hold PROP data.   There are 4 routines for allocating, examining,
  417. and freeing frame structures from a PROPList.
  418.     In order to link a Frame structure (i.e. an ILBMFrame) into this list, we
  419. need to "extend" the Frame with a few extra fields at the beginning of the
  420. structure.  We prepend the following fields to the frame, and refer to the
  421. whole thing as a PropFrame.
  422.  
  423.     ULONG        *NextPropFrame;    /* Pointer to the next frame in PROPList */
  424.     LONG        ifID;
  425.     USHORT    PropFrameSize;
  426.  
  427. So an ILBMPropFrame would be as follows:
  428.  
  429. typedef struct {
  430.     ULONG        *NextPropFrame;
  431.     LONG        ifID;
  432.     USHORT    PropFrameSize;
  433.     struct    ILBMFrame PropFrame; /* This is the data part of the PropFrame */
  434.     } ILBMPropFrame;
  435.  
  436.     So an ILBMPropFrame has three extra fields prepended to it. It is 10 bytes
  437. larger than an ILBMFrame.  The actual, imbedded Frame (i.e. after the first
  438. 3 fields) I'll refer to as the data part of the PropFrame.
  439.  
  440. In assembly, the prepended fields are:
  441.  
  442. Next    dc.l [address of next frame in list]
  443. ID        dc.b [the 4 byte type ID]    ;type of PropFrame
  444. Size    dc.w [size]                        ;the size of the entire structure (with the
  445.                                             ;subsequent data part)
  446.  
  447. And an ILBMPropFrame would be as follows:
  448.  
  449. Next    dc.l    0
  450. ID        dc.b    'ILBM'
  451. size    dc.w    SizeOfILBMFrame+10
  452.         ;an ILBMFrame structure immediately follows. This is the data part.
  453.  
  454.  
  455.     If an ILBM PROP (inside of a LIST) is encountered, the library always
  456. allocates an ILBMPropFrame on your behalf, linking it into the PROP list.
  457.     Note that the library ALWAYS takes care of parsing the following chunks in
  458. an ILBM PROP:
  459.  
  460.   BMHD  CMAP  CAMG  CRNG  CCRT
  461.  
  462. It parses the chunk data into the appropriate fields of the ILBMPropFrame. For
  463. other ILBM chunks (i.e. ANHD, DEST, etc), it will call your PROPhandler, pass-
  464. ing the chunkID and a pointer to the newly allocated ILBMPropFrame.  The
  465. PropID will be ID_ILBM.  You are expected to handle loading/parsing the chunk
  466. data, returning IFF_OKAY if successful.  You should either store the chunk's
  467. data somewhere, or use it to modify certain fields of the passed ILBMPropFrame.
  468. Returning any other negative IFFP error terminates LoadILBM() (or LoadIFF)
  469. which also returns that error.
  470.     For non-ILBM PROPs, your custom PROPhandler is also called.  The PropID is
  471. the type ID of the PROP (i.e. SMUS, 8SVX, SAMP).  The chunkID is meaningless.
  472. You should either return IFF_OKAY to ignore the PROP, or parse the PROP using
  473. the low level functions GetPChunkHdr and IFFReadBytes.  A convenient approach
  474. would be to allocate a PropFrame for the IFF type. (It's up to your program to
  475. define other types of PropFrames. What's an SMUSPropFrame look like? I don't
  476. know.)  Then link it into the PROPList.  Use GetPROPStruct() to do all this. 
  477. Then parse the PROP chunk data into this allocated PropFrame.  Later, your
  478. custom FORMhandler or NonILBMhandler can retreive the desired PropFrame via
  479. SearchPROP().  Your PROPhandler should return an IFFP code. Anything but
  480. IFF_OKAY aborts the load with that error.
  481.  
  482.     If no custom PROPhandler, non-ILBM PROPs and ILBM chunks ANHD, DEST, GRAB,
  483. SPRT, and DLTA are ignored.
  484.  
  485.  
  486.     ======================= GetPROPStruct ======================
  487.  Frame = GetPROPStruct(size,typeID,PROPList)
  488.    d0                   d0    d1      a1
  489.  
  490.     Allocates a PropFrame of passed size, and stores its ID, links it at the
  491. tail of the passed PROPList, and increments PROPList's entries. Returns a
  492. pointer to the PropFrame's data part (imbedded Frame), or 0 if error.
  493.     Note that a pointer to the data part is returned (i.e. skips those first
  494. 3 "extra" fields) so that you can treat the returned pointer just like a
  495. regular frame.  If you were allocating an ILBMPropFrame, the returned pointer
  496. would be that of an ILBMFrame.
  497.     The size must include the 3 extra fields.
  498.  
  499.  
  500.     ======================== SearchPROP() =====================
  501.  Frame = SearchPROP(ID,PROPList)
  502.   d0                d0    a1
  503.  
  504.     Searches the passed PROPList for the last PropFrame with the same ID as
  505. passed. If it finds one of the same type, returns the address of the data part
  506. (the imbedded frame), or 0 if none of that type found in the list.
  507.     Used by your custom FORMhandler to search the PROPList for any PropFrames
  508. with the same ID.
  509.  
  510.  
  511.     ======================== CopyILBMProp() ======================
  512.  CopyILBMProp(FromFrame,ToFrame)
  513.                   d0        a1
  514.  
  515.     Copies FromFrame to ToFrame. Both frames must be ILBM frames. It transfers
  516. the iUserFlags field of FromFrame to ToFrame without altering any set bits in
  517. ToFrame's iUserFlags.
  518.     If SearchPROP() returns an Frame, you'll want to copy that Frame to the
  519. Frame you intend to use to parse the FORM.  This is a routine for copying
  520. ILBMFrames. You'll need to write routines to copy other Frames that you devise.
  521.  
  522.  
  523.     ======================= FreePROPList() ======================
  524.  FreePROPList(PROPList)
  525.                 a1
  526.  
  527.     Frees all the PropFrames in the passed PROPList.  Normally, LoadILBM() and
  528. LoadIFF() do this.  When using LoadILBM() or LoadIFF(), the lib allocates and
  529. initializes the PROPList. Those routines also free up that PROPList and all its
  530. PropFrames upon termination of the load.  This function is provided in case you
  531. are maintaining a second PROPList that you create and need to free.
  532.  
  533.  
  534. *****************************************************************************
  535. 6). Custom FORM handler
  536.  
  537.     You'll need a custom FORM handler only if you aren't interested in ILBMs.
  538. In this case, you'll also use LoadIFF().
  539.     When the lib encounters a FORM, your custom FORM handler is passed a pointer
  540. to an initialized Context structure (to be discussed later).  Although no
  541. further initialization of this structure is required on your part, you may need
  542. it for calling certain low level lib functions. You are also passed your master
  543. ILBMFrame so that you can save data to it from within your FORMhandler. You are
  544. passed the type ID of the FORM (i.e. is it an 'ILBM'?).  Also, you are passed
  545. a pointer to the PROP list.  One of the first things you'll probably want to do
  546. is search it for any PropFrame with the same type ID as the FORM.  The function
  547. SearchPROP does this.  You may then copy that Frame's data into the Frame that
  548. is used to hold data within your FORMhandler.  CopyILBMProp() can copy an
  549. ILBMPropFrame to your master ILBMFrame.  Then, as you parse chunks in your
  550. FORMhandler, the new data will overwrite the corresponding PROP data (as it
  551. should).  You also are passed the Vectors structure address in case you need it
  552. further.  Your FORMhandler is expected to parse the FORM (using a few low level
  553. routines) and eventually return an IFFP code.  Anything but IFF_OKAY aborts the
  554. load.
  555.     You should use GetFChunkHdr() to read the header of each chunk inside a
  556. FORM.  You pass this the GroupContext structure (which the lib passed to your
  557. custom FORM handler).  It returns an IFFP code.  This should be the chunk ID
  558. if all went well.  Otherwise, it will be a negative IFFP number. (Remember,
  559. legal IFF IDs are positive numbers).
  560.  
  561.     ID = GetFChunkHdr(context)
  562.     d0                            a0
  563.  
  564.     The exception to this is ANIM files.  You should use GetF1ChunkHdr for
  565. ANIM files since ANIMs contain imbedded FORMs (ILBMs).  Note that this will
  566. cause the lib to reenter your custom FORM routine for each "frame" of the
  567. ANIM.  See IFFinfo.c for an example of a custom FORM routine handling ANIMs
  568. and ILBMs.  The return is the same as GetF1ChunkHdr.
  569.  
  570.     ID = GetF1ChunkHdr(context)
  571.     d0                            a0
  572.  
  573.  
  574.     ID = GetPChunkHdr(context)
  575.     d0                            a0
  576.  
  577.  
  578.  
  579. ******************************************************************************
  580. 7). Custom (ILBM) CHUNK Handler
  581.  
  582.  
  583.     You'll use a custom CHUNKhandler only when you also use the default FORM-
  584. handler.  By using a custom CHUNK handler, you determine what is done with
  585. each chunk in an ILBM.  You are passed the chunkID so that you can determine
  586. what you are dealing with (i.e. a CMAP, a DLTA, etc.).  If you don't want the
  587. chunk, simply return IFF_OKAY.  Otherwise, you are responsible for parsing
  588. the rest of the chunk.  The size is gotten via the passed GroupContext and the
  589. ChunkMoreBytes macro (see INCLUDE files).  NOTE: Do not use DOS Write() to
  590. read in bytes.  Always use IFFReadBytes().
  591.     If you would prefer the library to handle the chunk, return the chunkID.
  592. The library can handle CMAP, CRNG, CCRT, BMHD, CAMG, and BODY chunks.  For the
  593. first five chunks, the data is parsed into the ILBMFrame.  If a BMHD or a CAMG,
  594. the respective iFlags bit is set so that you'll know that this property was
  595. found.  For CMAP, the iNumColors reflects how many color regs were loaded into
  596. iColorTable.  For the BODY chunk, the lib decompresses the image into the
  597. ILBMFrames iWindow (or opens a window if NULL), sets the screen's colors to the
  598. iColorTable values, and terminates LoadILBM() with IFF_DONE.
  599.     You might not want the lib to decompress the BODY.  Perhaps you are writing
  600. an ANIM reader where you don't want the lib to actually load the first frame
  601. into some window's bitmap's planes.  You may prefer to decompress BODY and DLTA
  602. chunks during playback.  You can prevent the lib from doing this by handling
  603. ID_BODY yourself (in your CHUNKhandler) rather than returning this ID.
  604.     If you handle a chunk yourself, your CHUNKhandler should return IFF_OKAY
  605. to continue parsing the file, or any other negative IFFP number (including
  606. IFF_DONE) to stop the load.  It is recommended that you use IFF_DONE to
  607. successfully abort a load since this is what the default FORM handler
  608. does when it reaches an ILBM BODY.  This will insure that LoadILBM() always
  609. returns IFF_DONE for aborting the load successfully, END_MARK if you parsed the
  610. entire file without aborting, and other IFFP codes for error conditions.
  611.  
  612. NOTE: The ANIMFLAG bit of iUserFlags will be set if you are parsing an ANIM
  613.         FORM.  Remember that the lib calls your CHUNKhandler for ANHD and DLTA,
  614.         so for each DLTA you encounter, that is the end of another ANIM Frame
  615.         (imbedded ILBM FORM).  If ANIMFLAG is not set, then you are in a plain
  616.         ILBM file.
  617.  
  618.  
  619. ******************************************************************************
  620. 8). Low Level
  621.  
  622.     If the high level functions LoadIFFToWindow() and SaveWindowToIFF(), or
  623. the mid-level functions SaveILBM(), LoadILBM(), and LoadIFF() aren't suitable,
  624. you may use the low level routines to create a reader/writer.
  625.     You will have to keep track of LISTs, CATs, and PROPs as there are no
  626. Vectors Structure, PropFrames, and custom handlers at this level.
  627.     The low level routines are the same routines as the original EA code, IFFr.c
  628. and IFFw.c, except that they are smaller and faster asm modules.
  629.     At this point, I need to warn you that the arguments passed to some routines
  630. have been changed, as well as the order of the arguments. This was done to make
  631. the C interface code smaller and faster in assembly by using the movem in-
  632. struction. Mostly, the changes are trivial. In fact, the documentation for
  633. these functions is almost the original text.
  634.     Unless otherwise stated, the normal (successful) return codes for the low
  635. level routines is IFF_OKAY.  The Z and N Flags are set appropriately for low
  636. level functions. This means that assembly programmers can always bne to an
  637. error routine.
  638.     These routines ASSUME that they're the only ones reading/writing to the
  639. file.  You should only use these routines to read, write, or skip chunks in
  640. an IFF file.  Do not use DOS Read, Write, or Seek.
  641.     The library's low level routines utilize a 36 byte structure called a
  642. "GroupContext". The GroupContext structure that the library uses is identical
  643. to the original EA structure. Check the INCLUDE files for a description.
  644. The Frame field is now called UserData, but you can use it to pass a pointer
  645. to a Frame from level to sublevel.
  646.     For the high and mid-level routines, the lib allocates a new Context (and
  647. initializes it by OpenRIFF or OpenRGroup) for every group (FORM, CAT, LIST,
  648. or PROP) encountered. This is done for you even if you have custom vectors
  649. for FORMs and PROPs.  For low level, you need to do this yourself.  Any
  650. source code written with the original EA code may be used as an example.
  651.     You will only need to deal with Context structures if you are using the
  652. low level routines. This structure is for reading and writing groups and their
  653. chunks. It's just a linked node type of structure for reading (nested) chunks.
  654. A Context structure must be created whenever another level is encountered.
  655. The parentContext field contains the address of another Context structure if
  656. this is not the top level. For example, if a LIST is encountered, a Context
  657. structure is created. For the first FORM in the LIST, another context structure
  658. is created. The parentContext field of the FORM's Context would contain the
  659. address of the LIST's Context structure. The parentContext field of the LIST's
  660. Context structure would be NULL (as long as it wasn't inside of still amother
  661. group). You must read or skip over all the chunks in the FORM before you try to
  662. examine what comes next in the LIST. You can read a chunk via GetChunkHdr() and
  663. IFFReadBytes. You can skip a chunk by calling GetChunkHdr() a second time for
  664. the next chunk's ID.
  665.     The UserData field is set to the parent's Userdata field by certain
  666. routines. In this way, every Context's UserData is the same as its parentCon-
  667. text's UserData.
  668.     If you use the low level routines properly, the lib will take care of
  669. padding out chunks to even bytes, and keeping track of the total bytes read
  670. in or written out.
  671.     The bound field of the Context merits special mention in that it appears to
  672. have no useful purpose. If you place any value other than UNKNOWN there, it
  673. serves as the MAX number of bytes that can be written. So for example, if the
  674. bound = 10000, then you will be prevented from writing out a total of more than
  675. 10000 bytes. For most IFF applications, it isn't initially known how many bytes
  676. will eventually be written. All of the IFF writers which I've seen always set
  677. this to UNKNOWN.  Also, it doesn't seem useful to have the code impose a
  678. barrier.  AmigaDOS already returns an error if you attempt to write beyond a
  679. disc's capacity.  I can only assume that this "feature" was included so that
  680. this code would work on other computers with stranger DOS than the Amiga. This
  681. field is used in PutCkEnd to determine whether the lib needs to adjust UNKNOWN
  682. chunkSizes to the real value after it writes out a chunk. If EA had used a bit
  683. flag for UNKNOWN rather than a value to be compared against the bound field,
  684. then the code could be smaller and faster. Then again, the original code was
  685. written in C, a language invented by and for people who can't be bothered with
  686. trivial details like setting bits.
  687.  
  688.                     ========= Low Level Reader Routines ========
  689.  
  690.     For reading a chunk, the procedure is to allocate a Context structure,
  691.  initialize it with OpenRIFF or OpenRGroup, read the chunks with GetChunkHdr
  692.  (and its kin) and IFFReadBytes, and close the Context with CloseRGroup or
  693.  EndRGroup.
  694.  
  695.   IFFP = OpenRIFF(fileHandle, Context)
  696.    d0                 d1        a0
  697.  Given an open file, this initializes a Context spanning the whole file.
  698.  ASSUMES context was allocated by caller but not initialized.
  699.  ASSUMES caller doesn't deallocate the context before calling CloseRGroup.
  700.  Returns NOT_IFF IFFP code if the file is too small for even a chunk header.
  701.  
  702.  
  703.  FileSize = FileLength(fileHandle)
  704.     d0                    d1
  705.  Returns the length of the whole file or else a negative IFFP error code of
  706.  NO_FILE (fileHandle=0) or DOS_ERROR.  Does not change your current position
  707.  in the file.
  708.  
  709.  
  710.  IFFP = OpenRGroup(parent, new)   passed 2 Context structures
  711.   d0                 a0     a1
  712.  Open the remainder of the current chunk as a group read context.
  713.  This will be called just after the group's subtype ID has been read
  714.  (automatically by GetChunkHdr for LIST, FORM, PROP, and CAT) so the
  715.  remainder is a sequence of chunks.
  716.  This sets new's UserData = parent's UserData.
  717.  ASSUMES new context allocated by caller but not initialized.
  718.  ASSUMES caller doesn't deallocate the context or access the parent context
  719.  before calling CloseRGroup on the new context.
  720.  BAD_IFF error if context end is odd or extends past parent.
  721.  
  722.  
  723.  IFFP = CloseRGroup(context)
  724.   d0                  a0
  725.  Close a group read context, updating its parent context.
  726.  After calling this, the old context may be deallocated and the parent
  727.  context can be accessed again. It's okay to call this particular procedure
  728.  after an error has occurred reading the group.
  729.  
  730.  
  731.  ID = GetChunkHdr(context)
  732.  d0                  a0
  733.  Skip any remaining bytes of the previous chunk and any padding, then
  734.  read the next chunk header into context's chunkID and chunkSize fields.
  735.  If the chunkID is LIST, FORM, CAT, or PROP, this automatically reads the
  736.  subtype ID into context's subID field.
  737.  Caller should dispatch on groupID (and typeID) to an appropriate handler.
  738.  RETURNS the chunkID (the 4 ascii bytes of the new chunk header) if it found
  739.  another chunk. Otherwise, it will return one of the following errors:
  740.   1). END_MARK if there are no more chunks in this context
  741.   2). NOT_IFF if at the top level and it isn't a FORM, LIST, or CAT
  742.   3). BAD_IFF if a malformed chunk, the chunkSize is negative or too big for
  743.       containing context, ID isn't positive, or we hit end-of-file.
  744.  Note that if an error, bit #31 of d0 will be set and so you can either
  745.   1).  move.l d0,d1
  746.        bmi    to an error routine.
  747.   2).  btst.l #31,d0
  748.        bne    to an error routine.
  749.  See also GetFChunkHdr and GetPChunkHdr, below.
  750.  
  751.  
  752.  ID = GetFChunkHdr(context)
  753.  d0                        a0
  754.  Used by a FORM handler. It simply calls GetChunkHdr and returns BAD_IFF if
  755.  it encounters an imbedded PROP.
  756.  
  757.  
  758.  ID = GetPChunkHdr(context)
  759.  d0                        a0
  760.  Used by a PROP handler. It simply calls GetChunkHdr and returns BAD_IFF if
  761.  it encounters an imbedded PROP, LIST, CAT, or FORM.
  762.  
  763.  
  764.  IFFP = IFFReadBytes(nBytes, context, buffer)
  765.     d0                            d0            a0        a1
  766.  Read the specified number of data bytes of current chunk. (Use OpenGroup,
  767.  etc. instead to read the contents of a group chunk.) You can call this
  768.  several times to read the data piecemeal.
  769.  CLIENT_ERROR if nBytes < 0. SHORT_CHUNK if nBytes > remaining bytes
  770.  which could be due to a application bug or a chunk that's shorter than it
  771.  ought to be (bad form). (If CLIENT_ERROR or SHORT_CHUNK, IFFReadBytes won't
  772.  read any bytes.)
  773.  
  774.  
  775.  IFFP = SkipFwd(bytes, context)
  776.     d0                    d1            a0
  777.  Skip over bytes in a chunk. Won't go backwards.
  778.  Updates context's position but not context's bytesSoFar.
  779.  
  780.  
  781.  NOTE: The original EA code's SkipGroup() routine has been eliminated
  782.  since the library is set up to automatically parse LISTS and PROPS.
  783.  
  784.  
  785.  IFFP = GetCMAP(Context, colorMap, pNColorRegs)
  786.     d0                    d0             a0            a1
  787.  Reads in a CMAP chunk and creates the colorMap at passed address.
  788.  pNColorRegs is passed in as a pointer to the number of ColorRegisters
  789.  caller has space to hold.  GetCMAP sets to the number actually read.
  790.  
  791.  
  792.  IFFP = GetBODY( bitmap, mask, context, BMHD )
  793.     d0                    d0            d1        a0            a1
  794.  Reads the BODY into the passed bitmap's planes, decompressing if necessary.
  795.  Passed the addresses of a BitMap, mask plane (or NULL), the context
  796.  structure, and the loaded BitMap header chunk.
  797.  
  798.  
  799.  BOOL = UnPackRow(dstBytes0, srcBytes0, Source, Dest)
  800.     d0                        d1                d3            a2         a3
  801.    Converts data from "cmpByteRun1" run compression.
  802.     control bytes:
  803.      [0..127]   : followed by n+1 bytes of data.
  804.      [-1..-127] : followed by byte to be repeated (-n)+1 times.
  805.      -128       : NOOP.
  806. Unpacks one row, returning the source and destination addresses when it
  807. produces dstBytes bytes. This routine no longer uses POINTERS to POINTERS
  808. as in the original Elec Arts code. Also, args in a different order.
  809.  
  810.             =========== Low Level Writing (Save) Routines ==========
  811.  
  812.  These routines will random access back to set a chunk size value when the
  813.  caller doesn't know it ahead of time (UNKNOWN size).
  814.  
  815.  The overall scheme is to open an output Group Context via OpenWIFF or
  816.  OpenWGroup, call either PutCk or {PutCkHdr {IFFWriteBytes}* PutCkEnd} for
  817.  each chunk, then use CloseWGroup to close the Group Context.
  818.  
  819.  To write a group (LIST, FORM, PROP, or CAT), call StartWGroup, write out
  820.  its chunks, then call EndWGroup. StartWGroup automatically writes the
  821.  group header and opens a nested context for writing the contents.
  822.  EndWGroup closes the nested context and completes the group chunk.
  823.  
  824.  IFFP = OpenWIFF(limit, fileHandle, new)   new is a pointer to a GroupContext
  825.     d0                    d0            d1            a0     structure
  826.  Given a file open for output, initialize a new, write context.
  827.  The "limit" arg imposes a fence or upper limit on the logical file
  828.  position for writing data in this context. Pass in UNKNOWN to be limited
  829.  only by disk capacity.
  830.  ASSUMES new context structure allocated by caller but not initialized.
  831.  ASSUMES caller doesn't deallocate the context before calling CloseWGroup.
  832.  The caller is only allowed to write out one FORM, LIST, or CAT in this top
  833.  level context (see StartWGroup and PutCkHdr).
  834.  CLIENT_ERROR if limit is odd.
  835.  
  836.  
  837.  IFFP = StartWGroup(groupType, groupSize, subtype, parent, new)
  838.   d0                   d0         d1        d2        a0    a1
  839.  parent and new are the addresses of Context structures, groupType and
  840.  subtype are IDs, groupsize is a LONG
  841.  Start writing a group (presumably LIST, FORM, PROP, or CAT), opening a
  842.  nested context. The groupSize includes all nested chunks + the subtype ID.
  843.  The subtype of a LIST or CAT is a hint at the contents' FORM type(s). Pass
  844.  in FILLER ("    ") if it's a mixture of different kinds.
  845.  This writes the chunk header via PutCkHdr, writes the subtype ID via
  846.  IFFWriteBytes, and calls OpenWGroup. The caller may then write the nested
  847.  chunks and finish by calling EndWGroup.
  848.  The OpenWGroup call sets new's ILBMFrame to parent's ILBMFrame.
  849.  ASSUME new context structure allocated by caller but not initialized.
  850.  ASSUME caller doesn't deallocate the context or access the parent context
  851.  before calling CloseWGroup.
  852.  ERROR conditions: See PutCkHdr, IFFWriteBytes, OpenWGroup.
  853.  
  854.  
  855.  IFFP = OpenWGroup(parent, new)  parent and new are GroupContext structures
  856.   d0                 a0    a1
  857.  Open the remainder of the current chunk as a group write context.
  858.  This is normally only called by StartWGroup.
  859.  Any fixed limit to this group chunk or a containing context will impose
  860.  a limit on the new context.
  861.  This will be called just after the group's subtype ID has been written
  862.  so the remaining contents will be a sequence of chunks.
  863.  This sets new's UserData = parent's UserData.
  864.  ASSUME new context structure allocated by caller but not initialized.
  865.  ASSUME caller doesn't deallocate the context or access the parent context
  866.  before calling CloseWGroup.
  867.  CLIENT_ERROR if context end is odd or PutCkHdr wasn't called first.
  868.  
  869.  
  870.  IFFP = EndWGroup(old)
  871.   d0               a0
  872.  End a group started by StartWGroup.
  873.  This just calls CloseWGroup and PutCkEnd.
  874.  ERROR conditions: See CloseWGroup and PutCkEnd.
  875.  
  876.  
  877.  IFFP = CloseWGroup(old)  old is the address of a Context structure
  878.   d0                a0
  879.  Close a write context and update its parent context.
  880.  This is normally only called by EndWGroup.
  881.  If this is a top level context (created by OpenWIFF) we'll set the file's
  882.  EOF (end of file) but won't close the file.
  883.  After calling this, the old context may be deallocated and its parent
  884.  context can be accessed again.
  885.  Amiga DOS Note: There's no call to set the EOF. We just position to the
  886.  desired end and return. Caller must Close file at that position.
  887.  CLIENT_ERROR if PutCkEnd wasn't called first.
  888.  
  889.  
  890.  IFFP = PutCk(chunkID, chunkSize, context, data)
  891.   d0             d0       d1        a0      a1
  892.  Writes a whole chunk to a Context. This writes the chunk ID, chunkSize,
  893.  data bytes, and (if needed) a pad byte. It also updates the Context to
  894.  reflect how many bytes have been written to the file. Returns CLIENT_ERROR
  895.  if chunkSize = UNKNOWN. This is because you must know how many bytes of
  896.  data you wish to write in order to use this routine. (i.e. Use this routine
  897.  instead of a PutCkHdr/IFFWriteBytes/PutCkEnd series of calls when you know
  898.  exactly how many bytes will be in the chunk). See also PutCkHdr errors.
  899.  
  900.  
  901.  IFFP = PutCkHdr(chunkID, chunkSize, context)
  902.   d0                d0        d1        a0
  903.  Writes just an 8 byte chunk header. The chunk header consists of the 4 byte
  904.  ascii ID, and the chunkSize LONG. You should follow this will any number of
  905.  calls to IFFWriteBytes in order to write out the chunk data. Finally, when
  906.  all the chunk data is output, call PutCkEnd.
  907.  If you don't yet know how big the chunk is, pass in chunkSize = UNKNOWN,
  908.  then PutCkEnd will set the chunkSize for you later. This method is used
  909.  when you really don't know how many data bytes will eventually get written
  910.  out. (i.e. maybe you're compressing the data as it's being written out and
  911.  you don't want to bother with knowing or keeping track of how many bytes
  912.  have been written out. The lib does this for you as long as you specify
  913.  chunkSize = UNKNOWN and you use only the IFFWriteBytes routine to write data
  914.  to the file).
  915.  Otherwise, IFFWriteBytes and PutCkEnd will ensure that the specified
  916.  number of bytes get written.
  917.  CLIENT_ERROR if the chunk would overflow the Context's bound, if
  918.  PutCkHdr was previously called without a matching PutCkEnd, if chunkSize
  919.  < 0 (except UNKNOWN), if you're trying to write something other
  920.  than one FORM, LIST, or CAT in a top level (file level) context, or
  921.  if chunkID <= 0 (these illegal ID values are used for error codes).
  922.  
  923.  
  924.  IFFP = IFFWriteBytes(nBytes, context, data)
  925.   d0                    d0       a0     a1
  926.  Write nBytes number of data bytes for the current chunk and update the
  927.  Context.
  928.  Returns CLIENT_ERROR if any of the following conditions:
  929.    1). Writing nBytes of data would overflow the Context's limit or
  930.        current chunk's chunkSize. If you have specified these fields to be
  931.        UNKNOWN, then this condition is not applicable.
  932.    2). If PutCkHdr wasn't called first.
  933.    3). nBytes < 0 (i.e. ridiculously large).
  934.  
  935.  
  936.  IFFP = PutCkEnd(context)
  937.   d0               a0
  938.  Complete the current chunk, write a pad byte if needed, and update the
  939.  Context.
  940.  If current chunk's chunkSize = UNKNOWN, this goes back and sets the
  941.  chunkSize in the file.
  942.  CLIENT_ERROR if PutCkHdr wasn't called first, or if the application hasn't
  943.  written 'chunkSize' number of bytes with IFFWriteBytes.
  944.  
  945.  
  946.  IFFP = InitBMHdr(masking, compression, transparentColor,
  947.   d0               d0          d1              d2
  948.                 pageWidth, pageHeight, bmHdr0, bitmap)
  949.                     d3         d4        a0      a1
  950.  Initializes a BitMap Header (BMHD) chunk to the passed values, and sets the
  951.  BMHD's aspect ratio.
  952.  
  953.  
  954.  IFFP PutCMAP(depth, context, colorMap)
  955.   d0            d0     a0        a1
  956.  Writes out the passed colorMap (actually colorTable) as a CMAP chunk.
  957.  
  958.  
  959.  IFFP = PutBODY(mask, context, bmHdr, bitmap)
  960.   d0             d0     d1       a0     a1
  961.  Writes the BODY chunk to disk in compressed or uncompressed form.
  962.  
  963.  
  964.  bytes, newSource, newDest = PackRow(rowSize, pSource, pDest)
  965.   d0       a0         a1                d0       a0      a1
  966.  Given addresses of source and dest, packs one row, returning the new source
  967.  and destination addresses in a0 and a1. RETURNs count of packed bytes in d0.
  968.  Please note that this routine needs the actual addresses to the source and
  969.  destination buffers unlike the original EA code which wanted PTRS to PTRS.
  970.  That technique is unnecessary for assembly applications which can access
  971.  multiple return values in several registers. I decided to put the ineffic-
  972.  iency where it really belongs; in the C application. For C programmers, the
  973.  new source and dest addresses can be found at sourceptr and destptr respec-
  974.  tively. These are globals contained in the module ILBMInterface.asm which
  975.  must be assembled and linked with your application. These globals can be
  976.  accessed after a call to PackRow so that you'll have the addresses to pass
  977.  on the next, subsequent call. Of course, you'll be able to access the
  978.  returned number of packed bytes as a normal return. Ultimately what this
  979.  means is that if you call PackRow from a C application, you can never make
  980.  that application fully re-entrant (unless you put a FORBID/PERMIT around
  981.  the call).
  982.  
  983.  
  984. **************************************************************************
  985. 9). Adapting old IFF applications
  986.  
  987.     If you already have an old application that uses the Electronic Arts code,
  988. there are several approaches that you could take to adapt the program to use
  989. the ilbm lib instead.
  990.     The easiest approach is to ask yourself, "Can I use LoadIFFToWindow() or
  991. SaveWindowToIFF()?"  If all that you're doing with IFF is saving or loading
  992. ILBM pictures in standard Intuition windows, the answer is yes.  Get rid of ALL
  993. the EA code and INCLUDE files. Dump ReadPict.c, ReadIFF.c, IFFr.c, etc.  Follow
  994. the example ShowPic.c (or ShowPic.asm).  Then check the size of your program.
  995.     If you're doing ANIMs, non-ILBM (SMUS, 8SVX, etc.) or overscan screen views,
  996. you'll need to persue a second approach.  Use all of the low level routines of
  997. the lib in place of the similiarly named EA functions.  Section 5 of this
  998. document lists all of the low level functions (i.e. GetCMAP, OpenWGroup, PutCk,
  999. GetBODY, etc.)  Remember that these are functionally identical to the original
  1000. EA code.
  1001.     For example, you've probably got OpenRGroup() somewhere in your program.
  1002. (Check the EA code that you're using as well).  Found OpenRGroup()?  Did you
  1003. modify it?  No? Then you're all set.  Rip the function OpenRGroup() out of your
  1004. code. Add the line
  1005.  
  1006.     #include "ilbm_lib.h"
  1007.  
  1008. at the top of the source code. You are now using the ilbm lib's OpenRGroup()
  1009. instead of the original EA code.
  1010.     Do this with all of the low level routines you find throughout your program.
  1011.     Just be careful to note the order of the parameters. They may have changed,
  1012. and if so, you will have to modify any routine that calls the replaced func-
  1013. tion.  For example, a call to the EA routine, PutCkHdr() is as follows:
  1014.  
  1015.     PutCkHdr(context, ckID, ckSize);
  1016.  
  1017. The ilbm lib's PutCkHdr has a different order for those 3 args:
  1018.  
  1019.     PutCkHdr(ckID, ckSize, context);
  1020.  
  1021. Otherwise, the two functions are the same.  Several low level routines have a
  1022. different arg order.  This was done for the sake of efficiency in the lib's
  1023. C interface.  Also, note that a few low level functions no longer need
  1024. certain args.  For example, the EA GetBODY() is:
  1025.  
  1026.    GetBODY( context, bitmap, mask, bmHdr, buffer, bufsize );
  1027.  
  1028. The ilbm lib is:
  1029.  
  1030.     GetBODY( bitmap, mask, context, bmHdr );
  1031.  
  1032. The library supplies its own buffer. You don't have to. (That's one more thing
  1033. you can get rid of in your source code.)
  1034.     Finally, you should get rid of any EA INCLUDE files, and all INCLUDE
  1035. statements in your program that reference those files.  You don't need them.
  1036. "ilbm_lib.h" replaces all EA include files (i.e. IFF.h, PutPict.h, ReadPict.h,
  1037. Packer.h, and ILBM.h).  When you link your program, you will have to assemble
  1038. the file "ILBMInterface.asm", and link with that.
  1039.     Of course, if you can make use of the mid level functions by rewriting your
  1040. program to use all of their special features, your program will be that much
  1041. smaller and faster. Unfortunately, this is not as straightforward as substitu-
  1042. ting the low level functions.
  1043.  
  1044.  
  1045. *****************************************************************************
  1046. 10). ANIM Support
  1047.  
  1048.     There are a few routines to assist in the construction of an ANIM player.
  1049. These routines can unpack BODY and DLTA chunks, modifying a BitMap's planes
  1050. in order to construct the next frame of an animation. Also there is a routine
  1051. to setup a BitMap structure initially based on a loaded BMHD chunk.
  1052.     The normal procedure for an anim player would be to either replace the lib's
  1053. default 'FORM' handler with a custom handler, or use a CHUNKhandler set up to
  1054. handle ID_BODY, ID_ANHD, and ID_DLTA.  This handler should load the first
  1055. frame's BMHD and BODY, and the ANHD and DLTA for all subsequent frames. Also,
  1056. you should set up a PROPhandler to parse any ID_ANHD in an ILBM PROP.
  1057.     Then you can play back the anim as follows:
  1058.  
  1059.  1).    Allocate a raster large enough to hold the decompressed image. You can
  1060.         determine the size by the fields in the BMHD.
  1061.  
  1062.         height = BMHD's h or pageHeight, whichever is larger
  1063.         planedepth = numPlanes * height
  1064.         width = BMHD's w or pageWidth, whichever is larger
  1065.         RowBytes = (15+width)/8, rounded down to the nearest integer
  1066.         RasterSize = RowBytes * planeDepth
  1067.         get CHIP mem for the raster
  1068.  
  1069.  2).    Setup the BitMap structure to be used for displaying the ANIM by calling
  1070.         the ilbm lib's SetupBitMap. Pass your allocated raster to SetupBitMap.
  1071.         You will need to set up the View, ViewPort, and colorMap yourself.
  1072.  
  1073.  3).    Decompress the BODY into the BitMap's planes (i.e. your allocated
  1074.         raster) using the library's DecompBODY(). For double-buffered animation,
  1075.         you will need to setup an identical Bitmap/raster and copy the first
  1076.         image into the new raster.
  1077.  
  1078.  4).    Display the BitMap's planes (i.e. the first frame).
  1079.  
  1080.  5).    Modify the BitMap's planes for the next frame via DecompDLTA().
  1081.  
  1082.  6).  Repeat from step 4 until no more frames.
  1083.  
  1084.  
  1085.     Here are the 3 routines that you'll use in an ANIM player.
  1086.  
  1087.     ===================== SetupBitmap() ===================
  1088.  SetupBitmap(raster,bitmap,BMHD)
  1089.                d0     a0    a1
  1090.  
  1091.  Initializes passed bitmap's depth, rows, and BytesPerRow based on passed
  1092.  BMHD's (BitMapHeader) x, y, and numPlanes, then stores addresses into bitmap's
  1093.  planes[] of each plane within the passed raster. (All the planes in the raster
  1094.  form 1 contiguous CHIP mem block). Raster must be large enough for numPlanes
  1095.  * BMHD's (x+15)/8 * BMHD's y.
  1096.  After an ANIM is loaded, and you allocate a raster (via AllocRaster maybe)
  1097.  based on the BMHD's x, y, and numPlanes, you can use this routine to setup
  1098.  a BitMap structure in order to decompress the BODY with DecompBODY and
  1099.  start calling DecompDLTA.
  1100.  
  1101.  
  1102.             ================== DecompBODY() ===================
  1103.  DecompBODY(BODYdata, BMHD, Bitmap)
  1104.                     a0            a1        a2
  1105.  Decompresses a BODY chunk's data into the BitMap's planes based on the
  1106.  passed BMHD (BitMapHeader) chunk's w,y,numPlanes. This can be used to make
  1107.  the first frame of an ANIM.
  1108.  
  1109.  
  1110.             =================== DecompDLTA() ==================
  1111.  DecompDLTA(DLTAdata,Bitmap)
  1112.                     a0            a2
  1113.  Decompresses a DLTA chunk's data into the BitMap's planes (which must still
  1114.  have the previous frame's image). This routine calls MakeYTable and
  1115.  DecodeVKPlane, and can be used to "make" the next frame of an animation
  1116.  being double-buffered.
  1117.  
  1118.  
  1119.     You probably won't need to use these next 2 functions unless you're doing
  1120. something unusual with DLTA data.
  1121.  
  1122.         ===================== MakeYTable() =====================
  1123.  MakeYTable(width, height, table)
  1124.                     d0        d1         a0
  1125.  Makes a ytable for use with DecodeVKPlane. Table should be an memblock
  1126.  capable of holding 500 WORDs (1000 bytes).
  1127.  
  1128.  
  1129.     =================== DecodeVKPlane() ========================
  1130.        DecodeVKPlane(linebytes,ytable,in,out)
  1131.                         d0       d1   a0  a1
  1132.  
  1133.  By Jim Kent. Modified JG. Copyright 1987 Dancing Flame all rights reserved.
  1134.  Decompresses a DLTA chunk in vertical-byte-run-with-skips compression mode.
  1135.  
  1136.  where in is a bit-plane's worth of vertical-byte-run-with-skips data
  1137.  and out is a bit-plane that STILL has the image from last frame on it.
  1138.  Linebytes is the number of bytes-per-line (USHORT) in the out bitplane, and
  1139.  it should certainly be noted that the passed variable ytable must be
  1140.  initialized to point to a multiplication table of 0*linebytes, 1*linebytes
  1141.  ... n*linebytes  before this routine is called.
  1142.  Each entry in ytable is a USHORT.
  1143.  
  1144.  The format of "in":
  1145.    Each column of the bitplane is compressed separately.  A 320x200
  1146.    bitplane would have 40 columns of 200 bytes each.  The linebytes
  1147.    parameter is used to count through the columns, it is not in the
  1148.    "in" data, which is simply a concatenation of columns.
  1149.  
  1150.    Each columns is an op-count followed by a number of ops.
  1151.    If the op-count is zero, that's ok, it just means there's no change
  1152.    in this column from the last frame.
  1153.    The ops are of three classes, and followed by a varying amount of
  1154.    data depending on which class.
  1155.        1. Skip ops - this is a byte with the hi bit clear that says how many
  1156.           rows to move the "dest" pointer forward, ie to skip. It is non-
  1157.           zero
  1158.        2. Uniq ops - this is a byte with the hi bit set.  The hi bit is
  1159.           masked down and the remainder is a count of the number of bytes
  1160.           of data to copy literally.  It's of course followed by the
  1161.           data to copy.
  1162.        3. Same ops - this is a 0 byte followed by a count byte, followed
  1163.           by a byte value to repeat count times.
  1164.  
  1165.  
  1166. ***************************************************************************
  1167. 11). Image Size/Scaling
  1168.  
  1169.     When you ask the lib to load an image into an already opened window, some-
  1170. times the image won't be the exact same size as the window.  You may be trying
  1171. to load a HIRES picture (640 x 200) into a LORES screen (320 x 200), or vice
  1172. versa.  You may even be trying to load a picture larger than a standard Amiga
  1173. screen size (i.e. 660 x 220) into a standard size screen.  The library has a
  1174. routine that can automatically "scale" the picture to fit your window.  LORES
  1175. pictures will be "expanded" to fill a HIRES display, and HIRES will be "shrunk"
  1176. to fit a LORES.  Non-standard size pictures will likewise be scaled to fit a
  1177. standard screen.  Because of this, you never need worry about what size picture
  1178. a user has chosen to display in your window.  It will always fit.
  1179.     The lib's scaling routine makes multiple calls to graphics lib routines
  1180. which appear to do temporary mem alloc/dealloc.  Unfortunately, the dealloca-
  1181. tion is not done in the same order as the allocation. The net result: severe
  1182. memory fragmentation.  Oddly enough, this problem appears to only inflict LORES
  1183. pictures being scaled into a HIRES screen.  A HIRES picture into a LORES screen
  1184. works fine.  It appears likely that you may not be able to load a LORES picture
  1185. into a HIRES screen twice in a row.  Alas, the Amiga operating system is not
  1186. smart enough to re-coalese free blocks when it could.  You have to reboot the
  1187. computer.  You want smart memory coalesing, buy a MAC.  Despite this, I decided
  1188. that scaling the picture yielded more pleasing results than cropping it.  If
  1189. memory fragmentation problems persist, or you don't want a "smaller" picture to
  1190. be scaled to fill a "larger" display area, set the NOSCALE flag of the ILBM-
  1191. Frame's iUserFlags.  This will cause LORES pictures to only fill as much of a
  1192. larger display as they normally would.  Larger pictures are always scaled to
  1193. fit smaller displays.
  1194.     This scaling is meant to fit an image with a given width and height into
  1195. a screen with a different width and/or height.  The lib does not currently
  1196. adjust for different depths (number of planes).  For this reason, an image
  1197. with a depth of 5 will probably end up with "weird" colors when loaded into
  1198. a screen with a depth of 4.  Perhaps in the future, a routine will be added
  1199. to interpolate color tables if there appears to be interest in such a feature.
  1200.     Note that the process of scaling a picture to fit a different size screen
  1201. is notably slow.  Get used to it.
  1202.  
  1203.     Here is the routine that scales a section of one BitMap to fit into a
  1204. section of another RastPort.  It is passed a standard, graphics structure
  1205. called a Rectangle.
  1206.  
  1207. BOOL ScaleImage(dest_rectangle,source_rectangle,dest_rport,source_bitmap)
  1208.  d0                        a0                        a1                a2                a4
  1209.  
  1210. Passed pointers to the source bitmap, source rectangle structure (current
  1211. dimensions), destination rastport (to fit source into), and dest rectangle
  1212. struct (desired dimensions).  Scales the rectangular chunk (as described by
  1213. source_rectangle) of source bitmap into the rectangular chunk (as described by
  1214. dest_rectangle) of destination rastport.  It achieves this by remapping the
  1215. color value of each pixel, discarding or adding extra pixels per the scaling
  1216. dimensions using an array for the colors.  This does not create a larger or
  1217. smaller palette of colors if the # of planes is different. It simply fits one
  1218. image of given width and height into a different size raster. Alters the passed
  1219. sorce BitMap's planes while transfering. It only does one line at a time of the
  1220. source so it is SLOW.
  1221.  
  1222. Returns a 1 if success, 0 if error (no mem for color array).
  1223.  
  1224.     If you set the ILBMFrame's Flags ADJUSTVIEW bit and allow the library to
  1225. open the window/screen combo, the screen's view will be adjusted so that the
  1226. image is centered in the amiga's display.  This means that an overscanned image
  1227. (i.e. larger than 320 x 200 if LORES, 640 x 200 if HIRES) will be centered in
  1228. the display. This also takes INTERLACE and PAL into account. On the other hand,
  1229. an image smaller than a standard Intuition screen will be centered as well.
  1230.  
  1231.  
  1232. ***************************************************************************
  1233. 12). ILBMlib Error Msgs
  1234.  
  1235.     Your calling routines should check all returned IFFP error codes.  Don't
  1236. press on after an error!  The lib routines try to have no side effects if an
  1237. error, except that partial I/O is sometimes unavoidable.  Any routine could
  1238. return DOS_ERROR.  In that case, ask DOS for the specific error code, if
  1239. desired, via the DOS library's IOErr() routine.
  1240.  
  1241.  Here is a routine to help display error messages to the user. This routine
  1242.  returns a pointer to a NULL-terminated string which describes the IFFP error
  1243.  number. You can then display this string to the user. See the INCLUDE files
  1244.  for the meaning of certain IFFP codes.
  1245.  
  1246.  String = GetIFFPMsg(IFFP)
  1247.     d0                        d0
  1248.  
  1249.  
  1250. ****************************************************************************
  1251. 13). Misc Routines
  1252.  
  1253. Here's a routine to make the mouse pointer in the passed window "disappear".
  1254. Use Intuition's ClearPointer() or SetPointer() to change it again later.
  1255.  
  1256.     BlankPointer(window)
  1257.                         a0
  1258.  
  1259.  
  1260. ****************************************************************************
  1261. 14). Additional Comments
  1262.  
  1263.     An effort was made to test all of the lib functions in a variety of ways,
  1264. but I'm sure that there are things which have escaped me.  Please send any
  1265. bug reports to the above address.  I am not offering free consultation to
  1266. anyone, but if you have a particular problem or question, I will try to
  1267. assist.  If a particular piece of code appears not to work with the library,
  1268. I would be interested in seeing the code.  There will almost certainly be new,
  1269. improved lib versions in the future.  These will be sent to Fred Fish.
  1270.     An article in AmigaWorld's premiere Tech Journal has an assembly example of
  1271. using this library in an ANIM player, in which I demo using some ANIM oriented
  1272. routines of the library.
  1273.     The modules that you should have are as follows:
  1274.  
  1275.     ILBMLib.Doc            this text file
  1276.     ilbm.library        the library (to be copied to your boot disk's libs drawer)
  1277.     IFF.i                    assembly language include file
  1278.     ILBM_Lib.h            C Include file
  1279.     ShowPic.c            a C example of using the lib's high level functions
  1280.     ILBMInterface.asm the awful "poot" that is required for any C example
  1281.     BasicILBM            an AmigaBasic example
  1282.     ilbm.bmap            the bmap file for the Basic example
  1283.     ilbm_lib.fd            the Basic fd file
  1284.     BasicUsers            additional info for Basic programmers
  1285.     ShowPic.asm            an assembly example using the high level functions
  1286.     ANIMInfo.asm        an assembly example of installing a custom FORM handler
  1287.                             and using mid/low level functions
  1288.     ANIMInfo                an executable of the above
  1289.     ShowPic                a re-entrant ILBM viewer with color-cycling (operates
  1290.                             like CBM's "Display" program)
  1291.     IFFinfo.c            a C example of installing a custom FORM handler and using
  1292.                             mid/low level functions
  1293.     AboutIFF                a straightforward explanation of IFF file format    
  1294.     Play8SVX.c            a C example for using mid/low level routines to load an
  1295.                             8SVX file
  1296.  
  1297.  
  1298. *****************************************************************************
  1299. 15). Acknowledgements
  1300.  
  1301.     The ilbm.library is based upon much code placed in the public domain by
  1302. several individuals. The programmers who offer such favors are the ones
  1303. who are really responsible for the Amiga still being a viable product.
  1304.     Once again, I want to mention the people who helped make this public
  1305. domain offering possible:
  1306.  
  1307.     Jerry Morrison, Steve Shaw, and Steve Hayes, Electronic Arts.
  1308.     C. Scheppner, CATS.
  1309.     Jim Kent, Dancing Flame.
  1310.  
  1311.     This is one of those few efforts in which I didn't steal "something" from
  1312. Bryce Nesbitt.  Bryce is such a good programmer and documentator that it is
  1313. a fluke that he actually works for CBM.
  1314.  
  1315.  
  1316. *****************************************************************************
  1317. 16). Scary Legal Stuff
  1318.  
  1319.     "OK. IF I USE IT, WHAT'S IT GONNA COST ME."
  1320.  
  1321.     The ilbm.library may be used by and FREELY distributed with any application
  1322. be it commercial or public domain.
  1323.     There are no pagan users fees, Trump-esque licenses, or other forms of rabid
  1324. capitalist trickery associated with using this library and its support files.
  1325. You do not even have to acknowledge the secret of your expedient and memory
  1326. efficient IFF I/O routines.
  1327.     The only limitation is that you may not alter the actual executable of the
  1328. ilbm.library, nor sell the library and its support files as a distinct product
  1329. (i.e. represent it as such).
  1330.  
  1331.  
  1332. ============================= FOOTNOTES ===============================
  1333.  
  1334.  ¹    The routines that read and write data to disc (IFFReadBytes and IFFWrite-
  1335.     bytes) use unbuffered I/O. An application that uses the buffered version
  1336.     of EA's code will probably be faster but certainly not as memory effic-
  1337.     ient. Wouldn't you rather wait an extra 15 seconds to load an ANIM file
  1338.     rather than not be able to view it at all because the loader is sucking
  1339.     up RAM? Besides, if you want to waste memory with buffers that remain idle
  1340.     except during disk I/O, why not use the CLI ADDBUFFERS command. This way,
  1341.     you can speed up the lib to comparable levels with the buffered IFF code,
  1342.     and also allow other disc I/O code to benefit. I realize that the concept
  1343.     of conserving RAM is not popular amoung non-assembly programmers, most
  1344.     of whom write applications which unnecessarily require 1 MEG to run well.
  1345.  
  1346.  ²    In order to handle PROPs more efficiently and with less stack use than
  1347.     the original EA code, I made the following limitation.
  1348.         There should be no LIST imbedded within a LIST.
  1349.     In a file that violates this rule, PROP data from the outer LIST may
  1350.     affect chunks inside the inner LIST. Since I have never seen imbedded
  1351.     LISTs, I decided that such abominations don't deserve special treatment.
  1352.